/*
 * MovementBehaviour.h
 *
 *  Created on: 11.03.2012
 *      Author: ignacio
 */

#ifndef MOVEMENTBEHAVIOUR_H_
#define MOVEMENTBEHAVIOUR_H_

#include "Character.h"

namespace neurignacio{
/*
 * **************************************************************************************************************************************
 * 																BASIC MOVEMENT															*
 * **************************************************************************************************************************************
 */
/*
 * ***********************
 * BaseMovementBehaviour *
 * ***********************
 */
class BaseMovementBehaviour
{
	protected:
		int MovementBehaviour_ID;
		string MovementBehaviour_Description;
		virtual void init() = 0;

	public:
		// weight (i.e. importance) of this behaviour
		real weight;
		real priority;
	public:
		BaseMovementBehaviour() : weight(1), priority(0) {}
		BaseMovementBehaviour(real weight, real priority) : weight(weight), priority(priority) {}
		BaseMovementBehaviour(const BaseMovementBehaviour& behaviour) : MovementBehaviour_ID(behaviour.MovementBehaviour_ID), MovementBehaviour_Description(behaviour.MovementBehaviour_Description), weight(behaviour.weight), priority(behaviour.priority) {}
		BaseMovementBehaviour& operator=(const BaseMovementBehaviour& other)
		{
			if (this!=&other) {MovementBehaviour_ID=other.MovementBehaviour_ID; MovementBehaviour_Description=other.MovementBehaviour_Description; weight=other.weight; priority=other.priority;} return *this;
		}
		void setWeight(real w) {weight=w;}
		void setPriority(real p) {priority=p;}
		/**
		 * Works out the desired steering and writes it into the given
		 * steering output structure.
		 */
		virtual void getSteering(SteeringOutput& output) = 0;
		string MovementBehaviour_ID_Description() const {return MovementBehaviour_Description;}
		int getID() const {return MovementBehaviour_ID;}
	public:
		// operators for priority comparisons
		bool operator>(const BaseMovementBehaviour& behaviour) const {return this->priority>behaviour.priority;}
		bool operator<(const BaseMovementBehaviour& behaviour) const {return this->priority<behaviour.priority;}
		bool operator>=(const BaseMovementBehaviour& behaviour) const {return this->priority>=behaviour.priority;}
		bool operator<=(const BaseMovementBehaviour& behaviour) const {return this->priority<=behaviour.priority;}
		bool operator==(const BaseMovementBehaviour& behaviour) const {return this->priority==behaviour.priority;}
		bool operator!=(const BaseMovementBehaviour& behaviour) const {return this->priority!=behaviour.priority;}
		// operators comparisons against real numbers
		bool operator>(const real& number) const {return this->priority>number;}
		bool operator<(const real& number) const {return this->priority<number;}
		bool operator>=(const real& number) const {return this->priority>=number;}
		bool operator<=(const real& number) const {return this->priority<=number;}
		bool operator==(const real& number) const {return this->priority==number;}
		bool operator!=(const real& number) const {return this->priority!=number;}
};
/*
 * ***************
 * MuonCharacter *
 * ***************
 */
class MuonCharacter
{
public:
	/**
	 * The character who is moving is a muon
	 */
	Muon* character;
public:
	MuonCharacter() {}
	MuonCharacter(Muon* follower) : character(follower) {}
	MuonCharacter(const MuonCharacter& muonCharacter) : character(muonCharacter.character) {}
	MuonCharacter& operator=(const MuonCharacter& other)
	{
		if (this!=&other) {character=other.character;} return *this; // COPY BY REFERENCE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	}
	void setCharacter(Muon* follower) {character=follower;}
	void setCharacterAsCopy(Muon* follower) {character=follower;}
	void clone(MuonCharacter& follower) {setCharacterAsCopy(follower.character);};
};
/*
 * *******************
 * MovementBehaviour *
 * *******************
 */
class MovementBehaviour : public BaseMovementBehaviour, public MuonCharacter
// Movement Behaviour is a Base Movement Behaviour in which the Character that is moving is a muon
{
public:
	MovementBehaviour() : BaseMovementBehaviour(), MuonCharacter() {}
	MovementBehaviour(const MovementBehaviour& behaviour) : BaseMovementBehaviour(behaviour), MuonCharacter(static_cast<MuonCharacter>(behaviour)) {}
	MovementBehaviour(Muon* follower, real weight=1, real priority=0) : BaseMovementBehaviour(weight, priority), MuonCharacter(follower) {}
	MovementBehaviour& operator=(MovementBehaviour& other)
	{
		if (this!=&other) {*this=other; static_cast<MuonCharacter>(*this)=static_cast<MuonCharacter>(other);} return *this;
	}
};
typedef BaseContainer<MovementBehaviour> MovementBehaviourContainer;	// Stores a set of Movement Behaviours
typedef BasePointerContainer<MovementBehaviour> MovementBehaviourPointerContainer;

/*
 * ************************
 * MuonContainerCharacter *
 * ************************
 */
class MuonContainerCharacter
{
public:
	/**
	 * The character who is moving is a MuonContainer
	 */
	MuonContainer* characterContainer;		// COPY BY REFERENCE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public:
	MuonContainerCharacter() {}
	MuonContainerCharacter(const MuonContainerCharacter& container) : characterContainer(container.characterContainer) {}
	MuonContainerCharacter(MuonContainer* group) : characterContainer(group) {}
	MuonContainerCharacter& operator=(const MuonContainerCharacter& other)
	{
		if (this!=&other) {characterContainer=other.characterContainer;} return *this;
	}
	void setCharacterContainer(MuonContainer* group) {characterContainer=group;}
	void setCharacterContainerAsCopy(MuonContainer* group) {characterContainer=group;}
	void clone(MuonContainerCharacter& group) {setCharacterContainerAsCopy(group.characterContainer);}
};
}// end namespace neurignacio

namespace neurignacio{

// Holds 'timeToTarget' member
class TimeToTarget
{
public:
	/**
	 * At each step the character tries to reach its target in
	 * this duration. This means it moves more slowly when nearby.
	 */
	real timeToTarget;
public:
	TimeToTarget() : timeToTarget(0) {}
	TimeToTarget(real time) : timeToTarget(time) {}
	void setTimeToTarget(real time) {timeToTarget=time;}
	real getTimeToTarget(void) {return timeToTarget;}
};

// Holds 'maxPrediction' member
class MaxPrediction
{
public:

	real maxPrediction;
public:
	MaxPrediction() : maxPrediction(0) {}
	MaxPrediction(real time) : maxPrediction(time) {}
	void setMaxPrediction(real time) {maxPrediction=time;}
	real getMaxPrediction(void) {return maxPrediction;}
};

class ApproachLinearDistance
{
public:
	// Holds the approach (satisfaction) distance
	real targetRadius; 	// distance for terminating approach
	real slowRadius;	// distance for beginning to slow down
public:
	ApproachLinearDistance() : targetRadius(0), slowRadius(0) {}
	ApproachLinearDistance(real slow_Radius, real target_Radius) : slowRadius(slow_Radius), targetRadius(target_Radius) {}
	void setTargetRadius(real radius) {targetRadius=radius;}
	real getTargetRadius(void) {return targetRadius;}
	void setSlowRadius(real radius) {slowRadius=radius;}
	real getSlowRadius(void) {return slowRadius;}
	void setRadius(real slow_Radius, real target_Radius) {setSlowRadius(slow_Radius); setTargetRadius(target_Radius);}
};


class ApproachAngularDistance
{
public:
	// Holds the approach (satisfaction) distance
	angle targetAngularDistance; 	// distance for terminating approach (targetRadius)
	angle slowAngularDistance;	// distance for beginning to slow down (slowRadius)
public:
	ApproachAngularDistance() : targetAngularDistance(NULL_ANGLE), slowAngularDistance(NULL_ANGLE) {}
	ApproachAngularDistance(angle slow_Radius, angle target_Radius) : targetAngularDistance(slow_Radius), slowAngularDistance(target_Radius) {}
	void setTargetRadius(angle radius) {targetAngularDistance=radius;}
	angle getTargetRadius(void) {return targetAngularDistance;}
	void setSlowRadius(angle radius) {slowAngularDistance=radius;}
	angle getSlowRadius(void) {return slowAngularDistance;}
	void setRadius(angle slow_Radius, angle target_Radius) {setSlowRadius(slow_Radius); setTargetRadius(target_Radius);}
};
/**
 * The seek steering behaviour takes a target and aims right for
 * it with maximum acceleration.
 */
class TargetPosition
{
protected:
	/**
	 * The target may be any vector (i.e. it might be something
	 * that has no orientation, such as a point in space).
	 */
	Vector3* targetPosition;
public:
	TargetPosition() {}
	TargetPosition(Vector3* point) : targetPosition(point) {}
	TargetPosition(Location* followed) : targetPosition(&followed->position) {}
	void setTarget(Vector3* point) {targetPosition=point;}
	void setTarget(Location* followed) {targetPosition=(&followed->position);}
};

class TargetOrientation
{
public:		// MAKE PROTECTED!!!!!!!!!!!!!!!
	/**
	 * The target may be any orientation
	 */
	angle* targetOrientation;
public:
	TargetOrientation() {}
	TargetOrientation(angle* orientation) : targetOrientation(orientation) {}
	TargetOrientation(Location* followed) : targetOrientation(&followed->orientation) {}
	void setTarget(angle* orientation) {targetOrientation=orientation;}
	void setTarget(Location* followed) {targetOrientation=&followed->orientation;}
};

class TargetVelocity
{
protected:
	/**
	 * The target may be any vector (i.e. it might be something
	 * that has no orientation, such as a point in space).
	 */
	Vector3* targetVelocity;
public:
	TargetVelocity() {}
	TargetVelocity(Vector3* velocity) : targetVelocity(velocity) {}
	TargetVelocity(Lepton* followed) : targetVelocity(&followed->velocity) {}
	void setTarget(Vector3* point) {targetVelocity=point;}
	void setTarget(Lepton* followed) {targetVelocity=&followed->velocity;}
	string description() {return "TargetVelocity";}
};

class VirtualTargetPosition
{
protected:
	/**
	 * Holds the actual target we're aiming for. This can be written
	 * to by sub-classes (whereas the 'target' member cannot because
	 * it is const).
	 */
	Vector3* virtualTargetPosition;
public:
	/**
	 * Creates a new behaviour and target. This method is protected
	 * because this class isn't meant to be instantiated directly.
	 */
	VirtualTargetPosition() : virtualTargetPosition() {}
	VirtualTargetPosition(Vector3* point) : virtualTargetPosition(point) {}
	VirtualTargetPosition(Location* followed) : virtualTargetPosition(&followed->position) {}
	void setVirtualTarget(Vector3* point) {virtualTargetPosition=point;}
	void setVirtualTarget(Location* followed) {virtualTargetPosition=&followed->position;}
};

class VirtualTargetOrientation
{
protected:
	/**
	 * Holds the actual target we're aiming for. This can be written
	 * to by sub-classes (whereas the 'target' member cannot because
	 * it is const).
	 */
	angle* virtualTargetOrientation;

	/**
	 * Creates a new behaviour and target. This method is protected
	 * because this class isn't meant to be instantiated directly.
	 */
	VirtualTargetOrientation() : virtualTargetOrientation() {}
	VirtualTargetOrientation(angle* orientation) : virtualTargetOrientation(orientation) {}
	VirtualTargetOrientation(Location* followed) : virtualTargetOrientation(&followed->orientation) {}
	void setVirtualTarget(angle* orientation) {virtualTargetOrientation=orientation;}
	void setVirtualTarget(Location* followed) {virtualTargetOrientation=&followed->orientation;}
};

} // end namespace neurignacio
/****************************************************************************************
 *	 									GROUP BEHAVIOURS								*
 ****************************************************************************************/
namespace neurignacio{
class GroupMovementBehaviour : public BaseMovementBehaviour, public MuonContainerCharacter
{
// GroupMovementBehaviour Behaviour is a Base Movement Behaviour in which the Character that is moving is a muon container
public:
	GroupMovementBehaviour() : BaseMovementBehaviour(), MuonContainerCharacter() {}
	GroupMovementBehaviour(MuonContainer* follower, real weight=1, real priority=0) : BaseMovementBehaviour(weight, priority), MuonContainerCharacter(follower) {}
	void clone(const GroupMovementBehaviour& groupBehaviour) {*this=groupBehaviour;MuonContainerCharacter::setCharacterContainerAsCopy(groupBehaviour.characterContainer);}
};

} // end namespace neurignacio

#endif /* MOVEMENTBEHAVIOUR_H_ */
